#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stdint.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/file.h>

#include "core.h"
#include "board.h"
#include "user.h"
#include "debug.h"

/**
 * @brief   文件锁函数接口
 * @param   FILE* path, 文件指针; int type, 锁类型名
 * @return  int, 正确运行返回1 , 否则返回-1
 */
int file_lock_set(FILE* path_lock, int type)
{
    int fd_lock;
    struct flock lock;

	lock.l_type = type;
	lock.l_whence = SEEK_SET;
	lock.l_start = 0;
	lock.l_len = 0;
	lock.l_pid = -1;

    fd_lock = fileno(path_lock);

	fcntl(fd_lock, F_GETLK,&lock);

	if (lock.l_type != F_UNLCK) {
		if (lock.l_type == F_RDLCK)
			DEBUG("Read lock already set by %d\n", lock.l_pid);

		if (lock.l_type == F_WRLCK)
			DEBUG("Write lock already set by %d\n", lock.l_pid);
	}

	lock.l_type = type;
    lock.l_pid = getpid();

	if ((fcntl(fd_lock,F_SETLKW,&lock)) < 0) {
		DEBUG("Lock failed : type = %d\n", lock.l_type);
		return -1;
	}

	switch (lock.l_type) {
		case F_RDLCK:
			DEBUG("Read lock set by %d\n", getpid());
		    break;

		case F_WRLCK:
			DEBUG("write lock set by %d\n", getpid());
		    break;

		case F_UNLCK:
			DEBUG("Release lock by %d\n", getpid());
		    break;

		default:
		    break;
	}

	return 1;
}

/**
 * @brief   文件保存链表数据结构模块，
 *          1. 保存用户文件:save_file( board_list p_board_list, 
 *                  struct login_info * p_login_info, 
 *                  char * filename)
 *
 *          2. 阅读用户文件:read_file( board_list p_board_list, char * filename)
 * 
 * @param   1. p_board_list, p_login_info:用户数据链表, 为排行榜设置; filename, 文件名
 *          2. p_board_list:用户数据链表, 为排行榜设置; filename, 文件名
 * 
 * @return  1. board_list
 *          2. board_list
 */
board_list save_file( board_list p_board_list, 
                    struct login_info * p_login_info, 
                    char * filename)
{
    FILE *fp_data_file = NULL;
    int8_t ret = 0;
    board_list encipher_data;
    char *data;
    int data_length;

    if ((fp_data_file = fopen(filename, "wb")) == NULL ) {
        DEBUG("save_file() FROMET ERROR!\n");
    } else {

        file_lock_set(fp_data_file, F_WRLCK);

        while (p_board_list != NULL) {
            /*写入文件的内容加密*/
            strcpy(p_board_list->name, p_login_info->name);
            memcpy(&encipher_data, p_board_list, sizeof(board_list));
            data = (char *)&encipher_data;
            data_length = sizeof(board_list);
            ret = en_incipher(data, data_length);
            p_board_list = p_board_list->next;

            if (ret < 0) {
                DEBUG("用户信息加密失败\n");
                return NULL;
            }

            if (fwrite(&encipher_data, sizeof(board_list), 1, fp_data_file) != 1) {
                DEBUG("fwrite error");
                return NULL;
            }
        }

        file_lock_set(fp_data_file, F_UNLCK);
        fclose(fp_data_file);
    }
    return p_board_list;
}

board_list read_file( board_list p_board_list, 
                    char * filename)
{
    FILE *fp_data_file;
    int8_t ret = 0;
    board_list incipher_data;
    char *data;
    int data_length;

    if ((fp_data_file = fopen(filename, "rb")) == NULL ) {
        DEBUG("read_file() FROMET ERROR!\n");
    } else {

        file_lock_set(fp_data_file, F_WRLCK);

        while (!feof(fp_data_file)) {
            if (fread(&incipher_data, sizeof(board_list), 1, fp_data_file)) {

                /*将从文件中读取的内容解密*/
                data = (char *)&incipher_data;
                data_length = sizeof(board_list);
                ret = en_incipher(data, data_length);
                p_board_list = p_board_list->next;
                
                if (ret < 0) {
                    DEBUG("信息解密失败\n");
                    return NULL;
                }
            }
        }

        file_lock_set(fp_data_file, F_UNLCK);
        fclose(fp_data_file);
    }

    return p_board_list;

}


/**
 * @brief   简单加密及解密函数。
 * @param   data指针, data长度datalenght
 * @return  成功返回0, 失败返回-1
 */
int en_incipher(char *data, int data_length)
{
	uint8_t count;

	if (!data) {
        DEBUG("PARAM ERROR!\n");
        return -1;
    }

    for(count = 0; count < data_length; count++) {
        if(data[count] != '\0')
			data[count]^='!';
    }

    return 0;
}